/// Date utility for formatting and parsing dates.
/// 日期格式化和解析工具类
///
/// Original author: Sky24n (https://github.com/Sky24n)
/// Updated by: sunlunchang
library;

/// Common date format patterns.
/// 常用日期格式模式
///
/// Supports custom formats like 'yyyy/MM/dd HH:mm:ss', 'yyyy/M/d HH:mm:ss'.
/// 支持自定义格式，例如：'yyyy/MM/dd HH:mm:ss'，'yyyy/M/d HH:mm:ss'
///
/// Format requirements / 格式要求:
/// * year -> yyyy/yy   年份
/// * month -> MM/M    月份
/// * day -> dd/d      日期
/// * hour -> HH/H     小时
/// * minute -> mm/m   分钟
/// * second -> ss/s   秒
class DateFormats {
  /// Full format: 'yyyy-MM-dd HH:mm:ss'
  /// 完整格式：'yyyy-MM-dd HH:mm:ss'
  static String full = 'yyyy-MM-dd HH:mm:ss';

  /// Year-month-day-hour-minute: 'yyyy-MM-dd HH:mm'
  /// 年月日时分：'yyyy-MM-dd HH:mm'
  static String yMoDHM = 'yyyy-MM-dd HH:mm';

  /// Year-month-day: 'yyyy-MM-dd'
  /// 年月日：'yyyy-MM-dd'
  static String yMoD = 'yyyy-MM-dd';

  /// Year-month: 'yyyy-MM'
  /// 年月：'yyyy-MM'
  static String yMo = 'yyyy-MM';

  /// Month-day: 'MM-dd'
  /// 月日：'MM-dd'
  static String moD = 'MM-dd';

  /// Month-day-hour-minute: 'MM-dd HH:mm'
  /// 月日时分：'MM-dd HH:mm'
  static String moDHM = 'MM-dd HH:mm';

  /// Hour-minute-second: 'HH:mm:ss'
  /// 时分秒：'HH:mm:ss'
  static String hMS = 'HH:mm:ss';

  /// Hour-minute: 'HH:mm'
  /// 时分：'HH:mm'
  static String hM = 'HH:mm';

  /// Chinese full format: 'yyyy年MM月dd日 HH时mm分ss秒'
  /// 中文完整格式：'yyyy年MM月dd日 HH时mm分ss秒'
  static String zhFull = 'yyyy年MM月dd日 HH时mm分ss秒';

  /// Chinese full with English time: 'yyyy年MM月dd日 HH:mm:ss'
  /// 中文完整格式（英文时间）：'yyyy年MM月dd日 HH:mm:ss'
  static String zhFullEnTime = 'yyyy年MM月dd日 HH:mm:ss';

  /// Chinese year-month-day-hour-minute: 'yyyy年MM月dd日 HH时mm分'
  /// 中文年月日时分：'yyyy年MM月dd日 HH时mm分'
  static String zhYMoDHM = 'yyyy年MM月dd日 HH时mm分';

  /// Chinese year-month-day-hour-minute with English time: 'yyyy年MM月dd日 HH:mm'
  /// 中文年月日时分（英文时间）：'yyyy年MM月dd日 HH:mm'
  static String zhYMoDHMEnTime = 'yyyy年MM月dd日 HH:mm';

  /// Chinese year-month-day: 'yyyy年MM月dd日'
  /// 中文年月日：'yyyy年MM月dd日'
  static String zhYMoD = 'yyyy年MM月dd日';

  /// Chinese year-month: 'yyyy年MM月'
  /// 中文年月：'yyyy年MM月'
  static String zhYMo = 'yyyy年MM月';

  /// Chinese month-day: 'MM月dd日'
  /// 中文月日：'MM月dd日'
  static String zhMoD = 'MM月dd日';

  /// Chinese month-day-hour-minute: 'MM月dd日 HH时mm分'
  /// 中文月日时分：'MM月dd日 HH时mm分'
  static String zhMoDHM = 'MM月dd日 HH时mm分';

  /// Chinese month-day-hour-minute with English time: 'MM月dd日 HH:mm'
  /// 中文月日时分（英文时间）：'MM月dd日 HH:mm'
  static String zhMoDHMEnTime = 'MM月dd日 HH:mm';

  /// Chinese hour-minute-second: 'HH时mm分ss秒'
  /// 中文时分秒：'HH时mm分ss秒'
  static String zhHMS = 'HH时mm分ss秒';

  /// Chinese hour-minute: 'HH时mm分'
  /// 中文时分：'HH时mm分'
  static String zhHM = 'HH时mm分';
}

/// Days in each month (non-leap year).
/// 每月天数（非闰年）
const Map<int, int> monthDays = {
  1: 31,
  2: 28,
  3: 31,
  4: 30,
  5: 31,
  6: 30,
  7: 31,
  8: 31,
  9: 30,
  10: 31,
  11: 30,
  12: 31,
};

/// Date utility class providing various date/time operations.
/// 日期工具类，提供各种日期时间操作
class DateUtil {
  /// Gets [DateTime] from date string.
  /// 从日期字符串获取 DateTime 对象
  ///
  /// [dateStr] The date string to parse / 要解析的日期字符串
  /// [isUtc] Whether to convert to UTC (true) or local (false) / 是否转换为 UTC 时间
  /// Returns null if parsing fails / 解析失败返回 null
  static DateTime? getDateTime(String dateStr, {bool? isUtc}) {
    DateTime? dateTime = DateTime.tryParse(dateStr);
    if (isUtc != null) {
      if (isUtc) {
        dateTime = dateTime?.toUtc();
      } else {
        dateTime = dateTime?.toLocal();
      }
    }
    return dateTime;
  }

  /// Gets [DateTime] from milliseconds since epoch.
  /// 从毫秒时间戳获取 DateTime 对象
  ///
  /// [ms] Milliseconds since epoch / 自纪元以来的毫秒数
  /// [isUtc] Whether to interpret as UTC time / 是否解释为 UTC 时间
  static DateTime getDateTimeByMs(int ms, {bool isUtc = false}) {
    return DateTime.fromMillisecondsSinceEpoch(ms, isUtc: isUtc);
  }

  /// Gets milliseconds from date string.
  /// 从日期字符串获取毫秒时间戳
  ///
  /// [dateStr] The date string to parse / 要解析的日期字符串
  /// [isUtc] Whether to convert to UTC (true) or local (false) / 是否转换为 UTC 时间
  /// Returns null if parsing fails / 解析失败返回 null
  static int? getDateMsByTimeStr(String dateStr, {bool? isUtc}) {
    final dateTime = getDateTime(dateStr, isUtc: isUtc);
    return dateTime?.millisecondsSinceEpoch;
  }

  /// Gets current time in milliseconds since epoch.
  /// 获取当前时间的毫秒时间戳
  static int getNowDateMs() {
    return DateTime.now().millisecondsSinceEpoch;
  }

  /// Gets current date string in default format (yyyy-MM-dd HH:mm:ss).
  /// 获取当前日期字符串（默认格式：yyyy-MM-dd HH:mm:ss）
  static String getNowDateStr() {
    return formatDate(DateTime.now());
  }

  /// Formats date from milliseconds.
  /// 从毫秒时间戳格式化日期
  ///
  /// [ms] Milliseconds since epoch / 毫秒时间戳
  /// [isUtc] Whether to interpret as UTC time / 是否解释为 UTC 时间
  /// [format] Format pattern (uses [DateFormats.full] if not provided) / 格式模式（未提供时使用默认格式）
  static String formatDateMs(int ms, {bool isUtc = false, String? format}) {
    return formatDate(getDateTimeByMs(ms, isUtc: isUtc), format: format);
  }

  /// Formats date from date string.
  /// 从日期字符串格式化日期
  ///
  /// [dateStr] The date string to parse and format / 要解析和格式化的日期字符串
  /// [isUtc] Whether to convert to UTC (true) or local (false) / 是否转换为 UTC 时间
  /// [format] Format pattern (uses [DateFormats.full] if not provided) / 格式模式（未提供时使用默认格式）
  static String formatDateStr(String dateStr, {bool? isUtc, String? format}) {
    return formatDate(getDateTime(dateStr, isUtc: isUtc), format: format);
  }

  /// Formats [DateTime] to string.
  /// 将 DateTime 格式化为字符串
  ///
  /// [dateTime] The DateTime to format / 要格式化的 DateTime
  /// [format] Format pattern (uses [DateFormats.full] if not provided) / 格式模式（未提供时使用默认格式）
  /// Common formats available in [DateFormats] / 常用格式参见 [DateFormats]
  ///
  /// Format requirements / 格式要求:
  /// * year -> yyyy/yy   年份
  /// * month -> MM/M    月份
  /// * day -> dd/d      日期
  /// * hour -> HH/H     小时
  /// * minute -> mm/m   分钟
  /// * second -> ss/s   秒
  static String formatDate(DateTime? dateTime, {String? format}) {
    if (dateTime == null) return '';
    format = format ?? DateFormats.full;
    if (format.contains('yy')) {
      final year = dateTime.year.toString();
      if (format.contains('yyyy')) {
        format = format.replaceAll('yyyy', year);
      } else {
        format = format.replaceAll(
            'yy', year.substring(year.length - 2, year.length));
      }
    }

    format = _comFormat(dateTime.month, format, 'M', 'MM');
    format = _comFormat(dateTime.day, format, 'd', 'dd');
    format = _comFormat(dateTime.hour, format, 'H', 'HH');
    format = _comFormat(dateTime.minute, format, 'm', 'mm');
    format = _comFormat(dateTime.second, format, 's', 'ss');
    format = _comFormat(dateTime.millisecond, format, 'S', 'SSS');

    return format;
  }

  /// Common format helper.
  /// 通用格式化辅助方法
  static String _comFormat(
      int value, String format, String single, String full) {
    if (format.contains(single)) {
      if (format.contains(full)) {
        format =
            format.replaceAll(full, value < 10 ? '0$value' : value.toString());
      } else {
        format = format.replaceAll(single, value.toString());
      }
    }
    return format;
  }

  /// Gets weekday name.
  /// 获取星期几名称
  ///
  /// [dateTime] The DateTime to get weekday from / 要获取星期几的 DateTime
  /// [languageCode] Language code ('zh' or 'en') / 语言代码（'zh' 或 'en'）
  /// [short] Whether to use short format (Mon vs Monday, 周一 vs 星期一) / 是否使用短格式
  static String getWeekday(DateTime? dateTime,
      {String languageCode = 'en', bool short = false}) {
    if (dateTime == null) return '';
    String weekday = '';
    switch (dateTime.weekday) {
      case 1:
        weekday = languageCode == 'zh' ? '星期一' : 'Monday';
        break;
      case 2:
        weekday = languageCode == 'zh' ? '星期二' : 'Tuesday';
        break;
      case 3:
        weekday = languageCode == 'zh' ? '星期三' : 'Wednesday';
        break;
      case 4:
        weekday = languageCode == 'zh' ? '星期四' : 'Thursday';
        break;
      case 5:
        weekday = languageCode == 'zh' ? '星期五' : 'Friday';
        break;
      case 6:
        weekday = languageCode == 'zh' ? '星期六' : 'Saturday';
        break;
      case 7:
        weekday = languageCode == 'zh' ? '星期日' : 'Sunday';
        break;
      default:
        break;
    }
    return languageCode == 'zh'
        ? (short ? weekday.replaceAll('星期', '周') : weekday)
        : weekday.substring(0, short ? 3 : weekday.length);
  }

  /// Gets weekday name from milliseconds.
  /// 从毫秒时间戳获取星期几名称
  ///
  /// [milliseconds] Milliseconds since epoch / 毫秒时间戳
  /// [isUtc] Whether to interpret as UTC time / 是否解释为 UTC 时间
  /// [languageCode] Language code ('zh' or 'en') / 语言代码（'zh' 或 'en'）
  /// [short] Whether to use short format / 是否使用短格式
  static String getWeekdayByMs(int milliseconds,
      {bool isUtc = false, String languageCode = 'en', bool short = false}) {
    final dateTime = getDateTimeByMs(milliseconds, isUtc: isUtc);
    return getWeekday(dateTime, languageCode: languageCode, short: short);
  }

  /// Gets day of year (1-365/366).
  /// 获取一年中的第几天（1-365/366）
  ///
  /// [dateTime] The DateTime to get day of year from / 要获取天数的 DateTime
  /// Returns the day number in the year (e.g., Jan 1 = 1, Feb 1 = 32) / 返回在一年中的天数（例如：1月1日 = 1，2月1日 = 32）
  static int getDayOfYear(DateTime dateTime) {
    final year = dateTime.year;
    final month = dateTime.month;
    int days = dateTime.day;
    for (int i = 1; i < month; i++) {
      days = days + monthDays[i]!;
    }
    if (isLeapYearByYear(year) && month > 2) {
      days = days + 1;
    }
    return days;
  }

  /// Gets day of year from milliseconds.
  /// 从毫秒时间戳获取一年中的第几天
  ///
  /// [ms] Milliseconds since epoch / 毫秒时间戳
  /// [isUtc] Whether to interpret as UTC time / 是否解释为 UTC 时间
  static int getDayOfYearByMs(int ms, {bool isUtc = false}) {
    return getDayOfYear(DateTime.fromMillisecondsSinceEpoch(ms, isUtc: isUtc));
  }

  /// Checks if the given milliseconds represents today.
  /// 检查给定的毫秒时间戳是否为今天
  ///
  /// [milliseconds] Time to check / 要检查的时间
  /// [isUtc] Whether to interpret as UTC time / 是否解释为 UTC 时间
  /// [locMs] Optional reference time (uses current time if not provided) / 可选的参考时间（未提供时使用当前时间）
  static bool isToday(int? milliseconds, {bool isUtc = false, int? locMs}) {
    if (milliseconds == null || milliseconds == 0) return false;
    final old =
        DateTime.fromMillisecondsSinceEpoch(milliseconds, isUtc: isUtc);
    DateTime now;
    if (locMs != null) {
      now = DateUtil.getDateTimeByMs(locMs);
    } else {
      now = isUtc ? DateTime.now().toUtc() : DateTime.now().toLocal();
    }
    return old.year == now.year && old.month == now.month && old.day == now.day;
  }

  /// Checks if [dateTime] is yesterday relative to [locDateTime].
  /// 检查 dateTime 相对于 locDateTime 是否为昨天
  ///
  /// [dateTime] The date to check / 要检查的日期
  /// [locDateTime] The reference date (usually current date) / 参考日期（通常是当前日期）
  static bool isYesterday(DateTime dateTime, DateTime locDateTime) {
    if (yearIsEqual(dateTime, locDateTime)) {
      final spDay = getDayOfYear(locDateTime) - getDayOfYear(dateTime);
      return spDay == 1;
    } else {
      return ((locDateTime.year - dateTime.year == 1) &&
          dateTime.month == 12 &&
          locDateTime.month == 1 &&
          dateTime.day == 31 &&
          locDateTime.day == 1);
    }
  }

  /// Checks if milliseconds represents yesterday.
  /// 检查毫秒时间戳是否为昨天
  ///
  /// [ms] Time to check / 要检查的时间
  /// [locMs] Reference time / 参考时间
  static bool isYesterdayByMs(int ms, int locMs) {
    return isYesterday(DateTime.fromMillisecondsSinceEpoch(ms),
        DateTime.fromMillisecondsSinceEpoch(locMs));
  }

  /// Checks if the given time is in the current week.
  /// 检查给定时间是否在本周
  ///
  /// [ms] Time to check / 要检查的时间
  /// [isUtc] Whether to interpret as UTC time / 是否解释为 UTC 时间
  /// [locMs] Optional reference time (uses current time if not provided) / 可选的参考时间（未提供时使用当前时间）
  static bool isWeek(int? ms, {bool isUtc = false, int? locMs}) {
    if (ms == null || ms <= 0) {
      return false;
    }
    final oldDateTime = DateTime.fromMillisecondsSinceEpoch(ms, isUtc: isUtc);
    DateTime nowDateTime;
    if (locMs != null) {
      nowDateTime = DateUtil.getDateTimeByMs(locMs, isUtc: isUtc);
    } else {
      nowDateTime = isUtc ? DateTime.now().toUtc() : DateTime.now().toLocal();
    }

    final old = nowDateTime.millisecondsSinceEpoch >
            oldDateTime.millisecondsSinceEpoch
        ? oldDateTime
        : nowDateTime;
    final now = nowDateTime.millisecondsSinceEpoch >
            oldDateTime.millisecondsSinceEpoch
        ? nowDateTime
        : oldDateTime;
    return (now.weekday >= old.weekday) &&
        (now.millisecondsSinceEpoch - old.millisecondsSinceEpoch <=
            7 * 24 * 60 * 60 * 1000);
  }

  /// Checks if two dates are in the same year.
  /// 检查两个日期是否在同一年
  ///
  /// [dateTime] First date / 第一个日期
  /// [locDateTime] Second date / 第二个日期
  static bool yearIsEqual(DateTime dateTime, DateTime locDateTime) {
    return dateTime.year == locDateTime.year;
  }

  /// Checks if two millisecond timestamps are in the same year.
  /// 检查两个毫秒时间戳是否在同一年
  ///
  /// [ms] First timestamp / 第一个时间戳
  /// [locMs] Second timestamp / 第二个时间戳
  static bool yearIsEqualByMs(int ms, int locMs) {
    return yearIsEqual(DateTime.fromMillisecondsSinceEpoch(ms),
        DateTime.fromMillisecondsSinceEpoch(locMs));
  }

  /// Checks if the given date is in a leap year.
  /// 检查给定日期是否在闰年
  ///
  /// [dateTime] The date to check / 要检查的日期
  static bool isLeapYear(DateTime dateTime) {
    return isLeapYearByYear(dateTime.year);
  }

  /// Checks if the given year is a leap year.
  /// 检查给定年份是否为闰年
  ///
  /// [year] The year to check / 要检查的年份
  static bool isLeapYearByYear(int year) {
    return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
  }
}
